//
//  NSObject+FFBind.m
//  FFMVVM
//
//  Created by AKsoftware on 2016/11/1.
//  Copyright © 2016年 AKsoftware. All rights reserved.
//

#import "NSObject+FFBind.h"
#import <objc/runtime.h>

@implementation NSObject (FFBind)

+ (void)swizzlingOriginClass:(Class)originClass originMethodName:(NSString *)originMethodName
                currentClass:(Class)currentClass currentMethodName:(NSString *)currentMethodName
{
    SEL originSelector = NSSelectorFromString(originMethodName);
    SEL currentSelector = NSSelectorFromString(currentMethodName);
    Method originMethod = class_getInstanceMethod(originClass, originSelector);
    Method currentMethod = class_getInstanceMethod(currentClass, currentSelector);
    BOOL isSwizzed = class_addMethod(originClass, originSelector, method_getImplementation(currentMethod), method_getTypeEncoding(currentMethod));
    if (isSwizzed) {
        class_replaceMethod(originClass, currentSelector,method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
    } else {
        method_exchangeImplementations(originMethod, currentMethod);
    }
}

- (NSMutableArray *)bindArray
{
    if (!objc_getAssociatedObject(self,  @"bindArray")) {
        objc_setAssociatedObject(self,  @"bindArray", [NSMutableArray array], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return objc_getAssociatedObject(self,  @"bindArray");
}


- (void)addBindUinit:(FFBindUnit *)unit
{
    if ([[self bindArray] containsObject:unit]) {
        return;
    }
    [[self bindArray] addObject:unit];
}

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzlingOriginClass:self originMethodName:@"dealloc" currentClass:self currentMethodName:@"FFBind_delloc"];
    });
}

- (void)FFBind_delloc
{
    [self removeAllBindObject];
    [self FFBind_delloc];
}

#pragma removeObserver

- (void)removeAllBindObject
{
    [self removeBindUnitWithCondition:nil];
}

- (void)removeBindUnitWithCondition:(BOOL (^)(FFBindUnit *unit))condition
{
    if (objc_getAssociatedObject(self, @"bindArray")) {
        NSArray *array = [self bindArray];
        for (FFBindUnit *unit in array) {
            if (!condition || (condition && condition(unit))) {
                [unit destoryBind];
            }
        }
    }
}

//remove bind observer
- (void)removeAllBindObserver
{
    [self removeBindUnitWithCondition:^BOOL(FFBindUnit *unit) {
        return unit.target == self;
    }];
}

- (void)removeBindObserver:(NSObject *)observer
{
    [self removeBindUnitWithCondition:^BOOL(FFBindUnit *unit) {
        return unit.target == self && unit.observer == observer;
    }];
}

- (void)removeAllBindTarget
{
    [self removeBindUnitWithCondition:^BOOL(FFBindUnit *unit) {
        return (unit.observer == self);
    }];
}

- (void)removeBindTarget:(NSObject *)target
{
    [self removeBindUnitWithCondition:^BOOL(FFBindUnit *unit) {
        return unit.target == target && unit.observer == self;
    }];
}

#pragma add Observer

- (void)addBindObserver:(NSObject *)object
        forkeyPathArray:(NSArray *)keyPathArray
                options:(NSKeyValueObservingOptions)opthions
           performQueue:(dispatch_queue_t)queue
           performBlock:(void(^)(NSString *key, id value))block
{
    FFKVOBindUnit* uint = [[FFKVOBindUnit alloc] initTarget:self
                                                   observer:object
                                                    keyPath:keyPathArray
                                                    options:opthions
                                               performQueue:queue
                                               performBlock:block];
    uint.indentify = nil;
}

- (void)addBindObserver:(NSObject *)object
        forkeyPathArray:(NSArray *)keyPathArray
                options:(NSKeyValueObservingOptions)opthions
          performThread:(NSThread *)thread
           performBlock:(void(^)(NSString *key, id value))block
{
    FFKVOBindUnit* uint = [[FFKVOBindUnit alloc] initTarget:self
                                                   observer:object
                                                    keyPath:keyPathArray
                                                    options:opthions
                                              performThread:thread
                                               performBlock:block];
    uint.indentify = nil;
}



@end
